/*////////////////////////////////////////////////////////////////////////
*
*  Project:      mapgen
*  Description:  Map Generator for Planet Wars at http://ai-contest.com/.
*  Author:       Copyright (C) 2010 Alexander Makodzeba
*  License:      GPLv3
*
*  This file is part of mapgen.
*
*  mapgen is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  mapgen is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with mapgen.  If not, see <http://www.gnu.org/licenses/>.
*
////////////////////////////////////////////////////////////////////////*/

#include "StdAfx.h"
#include "MapGenerator.h"
#include "PlanetData.h"

//////////////////////////////////////////////////////////////////////////
// generates random integer in a selected range
int GenerateRandomInt(MinMaxPair range)
{
	return range.Min + rand() % (range.Max - range.Min + 1);
}

//////////////////////////////////////////////////////////////////////////
// generates random double in a selected range
double GenerateRandomDouble(double rangeStart, double rangeEnd)
{
	MinMaxNormalize<double>(rangeStart, rangeEnd);
	double delta = rangeEnd - rangeStart;
	double relative = rand()/( ( (double)RAND_MAX + 1 ) / delta );
	return rangeStart + relative;
}

//////////////////////////////////////////////////////////////////////////
// constructor
MapGenerator::MapGenerator(MapGeneratorParams params):
	_params(params)
{
}


//////////////////////////////////////////////////////////////////////////
// destructor
MapGenerator::~MapGenerator(void)
{
}

//////////////////////////////////////////////////////////////////////////
// generates all the maps
void MapGenerator::GenerateMaps(void)
{
	std:: cout << "maps generation started..." << std::endl;

	for(int i = 0; i<_params.mapsCount; i++)
	{
		std::string mapString;
		
		// generate planet count for this map
		int planetCount = GenerateRandomInt(_params.planetCount);

		if(_params.generateSymmetricMap)
		{
			// adjust planetCount to player count
			if(planetCount % _params.playersCount > 1)
			{
				//we allow only single planet in world center
				planetCount = planetCount / _params.playersCount + 1;
			}

			mapString = GenerateSymmetricMap(planetCount);
		}
		else
		{
			mapString = GenerateAsymmetricMap(planetCount);
		}

		// write created map to the file
		CreateMapFile(mapString, i);
	}

	std:: cout << "maps generation finished!" << std::endl;
}

//////////////////////////////////////////////////////////////////////////
// generate single symmetric map
std::string MapGenerator::GenerateSymmetricMap(int planetCount)
{
	int i, j;
	std::string map;

	//check if we need to put one planet in world center
	if(planetCount % _params.playersCount > 0)
	{
		//create center planet
		PlanetData planet = GenerateCenterPlanet();
		//write first planet to the map
		std::string stringPlanet = planet.ToString();
		map += stringPlanet + "\n";
	}

	//create symmetric planets for each player
	for(i = 0; i < planetCount; i += _params.playersCount)
	{
		PlanetData planet = GeneratePlanet();
		
		//put players on first planets in map
		if(i < _params.playersCount)
		{
			PutPlayerOnPlanet(planet, i+1); // i should be 0 but so it's more clear
		}

		//write planet to the map
		std::string stringPlanet = planet.ToString();
		map += stringPlanet + "\n";

		//generate symmetric planets
		for(j = 1; j < _params.playersCount; j++)
		{
			PlanetData symPlanet = GenerateSymmetricPlanet(planet, j);

			//put players on first planets in map
			if(i + j < _params.playersCount)
			{
				PutPlayerOnPlanet(symPlanet, i+j+1); // i should be 0 but so it's more clear
			}
			
			//write planet to the map
			std::string stringPlanet = symPlanet.ToString();
			map += stringPlanet + "\n";
		}

	}

	return map;
}

//////////////////////////////////////////////////////////////////////////
// generate single asymmetric map
std::string MapGenerator::GenerateAsymmetricMap(int planetCount)
{
	std::string map;

	for(int i = 0; i<planetCount; i++)
	{
		PlanetData planet = GeneratePlanet();
		//put players on first planets in map
		if(i<_params.playersCount)
		{
			PutPlayerOnPlanet(planet, i+1);
		}
		std::string stringPlanet = planet.ToString();
		map += stringPlanet + "\n";
	}

	return map;
}

//////////////////////////////////////////////////////////////////////////
// write map to the file
void MapGenerator::CreateMapFile(std::string map, int i)
{
	std::stringstream sstream;
	sstream << _params.mapFilePrefix;
	sstream << std::setw(3) << std::setfill('0') << i; //3 digits for number, like 001
	sstream << ".txt";
	std::ofstream mapFile;
	mapFile.open(sstream.str().c_str(), std::ios::binary);
	mapFile << map;
	mapFile.close();

	std:: cout << sstream.str().c_str() << std::endl;
}

//////////////////////////////////////////////////////////////////////////
// generate single random planet
PlanetData MapGenerator::GeneratePlanet()
{
	PlanetData planet;

	planet.size = GenerateRandomInt(_params.planetSize);
	planet.population = GenerateRandomInt(_params.planetPopulation);
	planet.position.x = GenerateRandomDouble(_params.pointWorldTopLeft.x, _params.pointWorldBottomRight.x);
	planet.position.y = GenerateRandomDouble(_params.pointWorldTopLeft.y, _params.pointWorldBottomRight.y);
	planet.owner = 0; // no owner by default

	return planet;
}

//////////////////////////////////////////////////////////////////////////
// generate planet, symmetric relative to the center of World
PlanetData MapGenerator::GenerateSymmetricPlanet(const PlanetData& planet, int nPlayer)
{
	PlanetData newPlanet = planet;

	if(_params.playersCount == 2) // simple way, map for 2 players
	{
		// simple symmetry
		newPlanet.position.x = _params.pointWorldCenter.x - planet.position.x + 
			(_params.pointWorldBottomRight.x - _params.pointWorldTopLeft.x);
		newPlanet.position.y = _params.pointWorldCenter.y - planet.position.y + 
			(_params.pointWorldBottomRight.y - _params.pointWorldTopLeft.y);
	}
	else
	{
		//TODO: do calculations for multiple players...
	}

	return newPlanet;
}

//////////////////////////////////////////////////////////////////////////
// generate planet right at center of the World
PlanetData MapGenerator::GenerateCenterPlanet()
{
	PlanetData planet;

	planet.size = GenerateRandomInt(_params.planetSize);
	planet.population = GenerateRandomInt(_params.planetPopulation);
	planet.position = _params.pointWorldCenter;
	planet.owner = 0; // no owner by default

	return planet;
}

//////////////////////////////////////////////////////////////////////////
// put specific player data for chosen planet
void inline MapGenerator::PutPlayerOnPlanet(PlanetData& planet, int playerID)
{
	planet.owner = playerID;
	planet.size = _params.playerPlanetSize;
	planet.population = _params.playerPlanetPopulation;
}
